/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许，不可做商业用途！
 *
 * 版权所有，侵权必究！
 */
package com.yami.shop.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.enums.DeliveryType;
import com.yami.shop.bean.enums.DvyType;
import com.yami.shop.bean.model.Delivery;
import com.yami.shop.bean.model.Order;
import com.yami.shop.bean.model.OrderItem;
import com.yami.shop.bean.model.UserAddrOrder;
import com.yami.shop.bean.param.OrderParam;
import com.yami.shop.bean.vo.UnDeliveryOrderExcelVO;
import com.yami.shop.common.config.Constant;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.i18n.I18nMessage;
import com.yami.shop.common.i18n.LanguageEnum;
import com.yami.shop.common.util.PoiExcelUtil;
import com.yami.shop.common.util.PrincipalUtil;
import com.yami.shop.dao.OrderMapper;
import com.yami.shop.service.DeliveryService;
import com.yami.shop.service.OrderExcelService;
import com.yami.shop.service.OrderService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Pineapple
 * @date 2021/7/21 15:37
 */
@Service
@RequiredArgsConstructor
public class OrderExcelServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderExcelService {

    @Value("${yami.expose.operation.auth:}")
    private Boolean permission;

    private final OrderMapper orderMapper;
    private final DeliveryService deliveryService;
    private final OrderService orderService;


    private final static int MIN_ROW = 2;
    private final static int FLOW_ID_MIN = 4;
    private final static int FLOW_ID_MAX = 30;

    @Override
    public void soldExcel(OrderParam orderParam, HttpServletResponse response) {
        if (Objects.isNull(orderParam.getLang())){
            orderParam.setLang(I18nMessage.getLang());
        }
        boolean isCn = orderParam.getLang().equals(LanguageEnum.LANGUAGE_ZH_CN.getLang());
        List<Order> orders = orderMapper.listOrdersDetailByOrderInfo(orderParam);

        //通过工具类创建writer
        ExcelWriter writer = ExcelUtil.getBigWriter();
        List<String> headerList;
        // 待发货
        String[] headerCn = {"订单编号", "下单时间", "收件人", "手机", "收货地址", "商品名称", "数量", "订单应付", "订单运费", "订单实付", "订单状态", "售后状态"};
        String[] headerEn = {"Order number", "The time of placing the order", "addressee", "phone", "Shipping address",
                "Product name", "number", "Order payable", "Order shipping costs", "Order paid-in", "Order status", "After-sales status"};
        headerList = Arrays.asList(isCn ? headerCn : headerEn);
        writer.merge(headerList.size() - 1, isCn ? "销售信息整理" : "Sales information collation");
        writer.writeRow(headerList);

        Sheet sheet = writer.getSheet();
        sheet.setColumnWidth(0, 20 * 256);
        sheet.setColumnWidth(1, 20 * 256);
        sheet.setColumnWidth(3, 20 * 256);
        sheet.setColumnWidth(4, 60 * 256);
        sheet.setColumnWidth(5, 60 * 256);

        int row = 1;
        for (Order dbOrder : orders) {
            UserAddrOrder addr = dbOrder.getUserAddrOrder();
            String addrInfo = "";
            if (Objects.nonNull(addr)) {
                addrInfo = addr.getProvince() + addr.getCity() + addr.getArea() + addr.getAddr();
            }
            if (Objects.equals(dbOrder.getDvyType(), DvyType.STATION.value())) {
                addrInfo = isCn ? "自提订单" : "Self-pickup orders";
            }
            List<OrderItem> orderItems = dbOrder.getOrderItems();
            int firstRow = row + 1;
            int lastRow = row + orderItems.size();
            int col = -1;
            // 订单编号
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getOrderNumber());
            // 下单时间
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getCreateTime());
            // 收件人
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getReceiverName());
            // "手机"
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, BooleanUtil.isFalse(permission) ? PhoneUtil.hideBetween(dbOrder.getReceiverMobile()).toString() : dbOrder.getReceiverMobile());
            // "收货地址"
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, addrInfo);
            int prodNameCol = ++col;
            int prodCountCol = ++col;
            for (OrderItem orderItem : orderItems) {
                row++;
                // 商品名称
                writer.writeCellValue(prodNameCol, row, orderItem.getProdName());
                // 数量
                writer.writeCellValue(prodCountCol, row, orderItem.getProdCount());
            }
            // 订单应付
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getTotal());
            // 订单运费
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getFreightAmount());
            // 订单实付
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getActualTotal());
            //订单状态
            String[] statusValueCn = {"", "待付款", "待发货", "待收货", "待评价", "成功", "失败", "待成团", ""};
            String[] statusValueEn = {"", "Pending payment", "Pending shipment", "To be received", "To be evaluated", "succeed", "fail", "To be organized", ""};
            String status = isCn ? statusValueCn[dbOrder.getStatus()] : statusValueEn[dbOrder.getStatus()];

            String refundStatus = "";
            String[] refundStatusValueCn = {"", "申请退款", "退款成功", "退款成功", "退款失败"};
            String[] refundStatusValueEn = {"", "Request a refund", "Refund successful", "Refund successful", "Refund failed"};
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, status);
            if (!Objects.isNull(dbOrder.getRefundStatus())) {
                refundStatus = isCn? refundStatusValueCn[dbOrder.getRefundStatus()] : refundStatusValueEn[dbOrder.getRefundStatus()];
            } else {
                refundStatus = isCn ? "暂无售后" : "There is no after-sales service";
            }
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, refundStatus);
        }
        PoiExcelUtil.writeExcel(response, writer);
    }

    @Override
    public void unDeliveryOrderExcel(OrderParam orderParam, HttpServletResponse response) {
        orderParam.setLang(I18nMessage.getDbLang());
        List<UnDeliveryOrderExcelVO> orderExcelList = orderMapper.listUnDeliveryOrderExcel(orderParam);
        handleExportGiveawayList(orderExcelList);
        //创建writer
        ExcelWriter writer = ExcelUtil.getBigWriter();
        List<String> orderHeaderList;
        String[] orderHeaderCn = {"订单号", "配送方式*", "快递公司名称*（配送方式为快递配送时必填）", "快递单号*（配送方式为快递配送时必填）", "收货人姓名", "收货人手机号", "收货地址", "商品名称", "sku名称", "数量", "赠品名称", "赠品数量"};
        String[] orderHeaderEn = {"Order Number", "Delivery method", "Express company name(required when the delivery method is express delivery)", "Express delivery number", "Consignee's name", "Receiving Address"
                , "Product name", "Sku name", "Count", "Giveaway Name", "Giveaway Count"};
        orderHeaderList = Arrays.asList(Objects.equals(I18nMessage.getDbLang(), 0) ? orderHeaderCn : orderHeaderEn);
        Sheet sheet = writer.getSheet();
        setSheetColumnWidth(sheet);
        writer.merge(orderHeaderList.size() - 1, "待发货订单信息整理");
        writer.writeRow(orderHeaderList);
        //配送方式
        String[] dvyTypes = {"快递配送", "无需快递", "同城配送"};
        PoiExcelUtil.createDropDownList(sheet, dvyTypes, 2, 50000, 1, 1);
        //快递公司
        List<Delivery> deliveryList = deliveryService.list(new LambdaQueryWrapper<>());
        if (CollUtil.isNotEmpty(deliveryList)) {
            String[] dvyName = new String[deliveryList.size()];
            for (int i = 0; i < deliveryList.size(); i++) {
                dvyName[i] = deliveryList.get(i).getDvyName();
            }
            PoiExcelUtil.createDropDownList(sheet, dvyName, 2, 50000, 2, 2);
        }
        //写入数据
        int row = 1;
        for (UnDeliveryOrderExcelVO orderExcel : orderExcelList) {
            List<OrderItem> orderItemList = orderExcel.getOrderItemList();
            List<OrderItem> giveawayList = orderItemList.stream().filter(o -> CollUtil.isNotEmpty(o.getGiveawayList())).flatMap(orderItem -> orderItem.getGiveawayList().stream()).collect(Collectors.toList());
            List<OrderItem> countList = orderItemList.stream().filter(o -> CollUtil.isEmpty(o.getGiveawayList())).collect(Collectors.toList());
            int firstRow = row + 1;
            int lastRow = row + giveawayList.size() + countList.size();
            int col = -1;
            //订单号
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, orderExcel.getOrderNumber());
            //配送方式
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, DeliveryType.getDescription(orderExcel.getDvyType()));
            //快递公司名称
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, orderExcel.getDvyName());
            //快递单号
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, orderExcel.getDvyFlowId());
            //收货人姓名
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, orderExcel.getReceiver());
            //收货人手机号
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, orderExcel.getMobile());
            //收货地址
            PoiExcelUtil.mergeIfNeed(writer, firstRow, lastRow, ++col, col, orderExcel.getReceivingAddr());
            for (OrderItem orderItem : orderItemList) {
                int itemFirstRow = row + 1;
                int index = 6;
                row++;
                int itemLastRow = CollUtil.isEmpty(orderItem.getGiveawayList()) ? row : itemFirstRow + orderItem.getGiveawayList().size() - 1;
                //商品名称
                PoiExcelUtil.mergeIfNeed(writer, row, itemLastRow, ++index, index, orderItem.getProdName());
                //sku名称
                PoiExcelUtil.mergeIfNeed(writer, row, itemLastRow, ++index, index, orderItem.getSkuName());
                //商品待发货数量
                PoiExcelUtil.mergeIfNeed(writer, row, itemLastRow, ++index, index, orderItem.getChangeNum());
                if (CollUtil.isNotEmpty(orderItem.getGiveawayList())) {
                    for (OrderItem giveaway : orderItem.getGiveawayList()) {
                        int giveIndex = 9;
                        //赠品名称
                        writer.writeCellValue(++giveIndex, row, giveaway.getProdName());
                        //赠品数量
                        writer.writeCellValue(++giveIndex, row, giveaway.getChangeNum());
                        row++;
                    }
                    row--;
                }
            }
        }
        PoiExcelUtil.writeExcel(response, writer);
    }

    private void setSheetColumnWidth(Sheet sheet) {
        sheet.setColumnWidth(0, 20 * 256);
        sheet.setColumnWidth(1, 20 * 256);
        sheet.setColumnWidth(2, 40 * 256);
        sheet.setColumnWidth(3, 20 * 256);
        sheet.setColumnWidth(4, 20 * 256);
        sheet.setColumnWidth(5, 20 * 256);
        sheet.setColumnWidth(6, 60 * 256);
        sheet.setColumnWidth(7, 80 * 256);
        sheet.setColumnWidth(9, 10 * 256);
        sheet.setColumnWidth(10, 80 * 256);
    }

    private void handleExportGiveawayList(List<UnDeliveryOrderExcelVO> orderExcelList) {
        for (UnDeliveryOrderExcelVO order : orderExcelList) {
            // 处理赠品列表
            Map<Long, List<OrderItem>> giveawayMap = order.getOrderItemList().stream().filter(orderItem -> Objects.nonNull(orderItem.getGiveawayOrderItemId())).collect(Collectors.groupingBy(OrderItem::getGiveawayOrderItemId));
            if (MapUtil.isNotEmpty(giveawayMap)) {
                Iterator<OrderItem> iterator = order.getOrderItemList().iterator();
                while (iterator.hasNext()) {
                    OrderItem orderItem = iterator.next();
                    if (Objects.nonNull(orderItem.getGiveawayOrderItemId())) {
                        iterator.remove();
                    }
                    orderItem.setGiveawayList(giveawayMap.get(orderItem.getOrderItemId()));
                }
            }
        }
    }

    private void handleImportGiveawayList(List<OrderItem> orderItems) {
        // 处理赠品列表
        Map<Long, List<OrderItem>> giveawayMap = orderItems.stream().filter(orderItem -> Objects.nonNull(orderItem.getGiveawayOrderItemId())).collect(Collectors.groupingBy(OrderItem::getGiveawayOrderItemId));
        if (MapUtil.isNotEmpty(giveawayMap)) {
            Iterator<OrderItem> iterator = orderItems.iterator();
            while (iterator.hasNext()) {
                OrderItem orderItem = iterator.next();
                if (Objects.nonNull(orderItem.getGiveawayOrderItemId())) {
                    iterator.remove();
                }
                orderItem.setGiveawayList(giveawayMap.get(orderItem.getOrderItemId()));
            }
        }
    }

    @Override
    public String exportOrderExcel(MultipartFile file, Long shopId, List<UnDeliveryOrderExcelVO> orderList) throws Exception {
        try (InputStream inputStream = file.getInputStream();
             Workbook workbook = WorkbookFactory.create(inputStream)) {
            //读取工作表
            Sheet sheet = workbook.getSheetAt(0);
            int allRow = sheet.getPhysicalNumberOfRows();
            //无内容
            if (allRow <= MIN_ROW) {
                throw new YamiShopBindException("yami.document.no.content");
            }
            // * 2.1不是Excel文件，直接返回提示信息
            if (!(file.getOriginalFilename().contains(Constant.XLS_FILE) || file.getOriginalFilename().contains(Constant.XLSX_FILE))) {
                // 格式错误
                throw new YamiShopBindException("yami.file.error");
            }
            //进行数据校验
            List<Delivery> deliveryList = deliveryService.list(new LambdaQueryWrapper<>());
            Map<String, Long> deliveryCompanyMap = deliveryList.stream().collect(Collectors.toMap(Delivery::getDvyName, Delivery::getDvyId));
            int dvyType = 0;
            int step = 1;
            for (int i = 2; i < allRow; i += step) {
                UnDeliveryOrderExcelVO unDeliveryOrderExcelVO = new UnDeliveryOrderExcelVO();
                int trueStep = 1;
                try {
                    trueStep = checkExcel(sheet, i, dvyType, unDeliveryOrderExcelVO, shopId, deliveryCompanyMap, trueStep);
                    step = trueStep;
                } catch (Exception e) {
                    //错误信息提示
                    return "第" + (i + 1) + "行数据有错误," + e.getMessage();
                }
                orderList.add(unDeliveryOrderExcelVO);
            }
            return null;
        } catch (Exception e) {
            return e.getMessage();
        }

    }

    private int checkExcel(Sheet sheet, int i, int dvyType, UnDeliveryOrderExcelVO unDeliveryOrderExcelVO, Long shopId, Map<String, Long> deliveryCompanyMap, int trueStep) {
        Row row = sheet.getRow(i);
        int cellRow = 0;
        String orderNumber = PoiExcelUtil.getRowValue(row, cellRow, false);
        if (Objects.isNull(orderNumber) || orderNumber.length() == 0) {
            //订单不存在
            throw new YamiShopBindException("yami.order.no.exist");
        }
        UnDeliveryOrderExcelVO order = orderService.getOrderAndOrderItemsByOrderNumberAndShopId(orderNumber, shopId);
        if (Objects.isNull(order)) {
            // 订单不存在
            throw new YamiShopBindException("yami.order.no.exist");
        }
        for (OrderItem orderItem : order.getOrderItemList()) {
            boolean unDelivery = Objects.equals(orderItem.getStatus(), 0) || (!Objects.equals(orderItem.getProdCount(), orderItem.getStatus()) && !Objects.equals(orderItem.getStatus(), -1));
            if (unDelivery) {
                //如果有订单项发货了，则不允许批量发货
                throw new YamiShopBindException("yami.order.is.delivery");
            }
        }
        String deliveryType = PoiExcelUtil.getRowValue(row, ++cellRow, false);
        if (Objects.isNull(deliveryType) || deliveryType.length() == 0) {
            //请选择配送方式
            throw new YamiShopBindException("yami.product.dvy.type");
        } else if (Objects.equals(deliveryType, DeliveryType.NO_EXPRESS.getDesc())) {
            dvyType = DeliveryType.NO_EXPRESS.getValue();
        } else if (Objects.equals(deliveryType, DeliveryType.SAME_CITY.getDesc())) {
            dvyType = DeliveryType.SAME_CITY.getValue();
        } else if (Objects.equals(deliveryType, DeliveryType.EXPRESS.getDesc())) {
            dvyType = DeliveryType.EXPRESS.getValue();
        }
        if (Objects.equals(dvyType, DeliveryType.SAME_CITY.getValue()) && !Objects.equals(order.getDvyType(), DeliveryType.SAME_CITY.getValue())) {
            //当前订单不支持同城配送
            throw new YamiShopBindException("yami.order.same.city.error3");
        }
        //导入的配送方式与订单的配送方式不一致
        //订单的配送方式为快递配送时，导入的配送方式可选择快递配送和无需快递，其他情况订单的配送方式与导入的配送方式需保持一致
        if(!Objects.equals(order.getDvyType(), dvyType) && !Objects.equals(order.getDvyType(), DeliveryType.EXPRESS.getValue())){
            throw new YamiShopBindException("yami.order.devType.notMatch");
        }else if(!Objects.equals(order.getDvyType(), dvyType) && Objects.equals(order.getDvyType(), DeliveryType.EXPRESS.getValue())){
            if(!Objects.equals(dvyType, DeliveryType.NO_EXPRESS.getValue())){
                throw new YamiShopBindException("yami.order.devType.notMatch");
            }
        }
        String dvyName = PoiExcelUtil.getRowValue(row, ++cellRow, false);
        String dvyFlowId = PoiExcelUtil.getRowValue(row, ++cellRow, false);
        if (Objects.equals(deliveryType, DeliveryType.EXPRESS.getDesc())) {
            //只有快递需要校验快递信息
            if (Objects.isNull(dvyName) || dvyName.length() == 0) {
                //快递公司名称不能为空
                throw new YamiShopBindException("yami.order.no.dvyName");
            }
            dvyFlowId = StringUtils.deleteWhitespace(dvyFlowId);
            if (StrUtil.isBlank(dvyFlowId) || dvyFlowId.length() < FLOW_ID_MIN || dvyFlowId.length() > FLOW_ID_MAX
                    || !PrincipalUtil.isMatching(PrincipalUtil.WITHOUT_CHINESE, dvyFlowId)) {
                //快递单号格式错误
                throw new YamiShopBindException("yami.order.delivery.number");
            }
            unDeliveryOrderExcelVO.setDvyFlowId(dvyFlowId);
            unDeliveryOrderExcelVO.setDvyName(dvyName);
            unDeliveryOrderExcelVO.setDvyId(deliveryCompanyMap.get(dvyName));
        }
        unDeliveryOrderExcelVO.setOrderNumber(orderNumber);
        unDeliveryOrderExcelVO.setDvyType(dvyType);
        List<OrderItem> orderItems = order.getOrderItemList();
        //处理有赠品的订单项，防止跳过多个表格行
        List<OrderItem> orderItemes = new ArrayList<>();
        orderItemes.addAll(orderItems);
        handleImportGiveawayList(orderItemes);
        //已经全部发货的就不加入到待发货订单项
        orderItems.removeIf(orderItem -> Objects.equals(orderItem.getStatus(), 0));
        orderItemes.removeIf(orderItem -> Objects.equals(orderItem.getStatus(), 0));
        List<OrderItem> giveawayList = orderItemes.stream().filter(o -> CollUtil.isNotEmpty(o.getGiveawayList())).flatMap(orderItem -> orderItem.getGiveawayList().stream()).collect(Collectors.toList());
        List<OrderItem> countList = orderItemes.stream().filter(o -> CollUtil.isEmpty(o.getGiveawayList())).collect(Collectors.toList());
        if (orderItemes.size() > 0) {
            trueStep = giveawayList.size() + countList.size();
        }
        unDeliveryOrderExcelVO.setOrderItemList(orderItems);
        return trueStep;
    }
}
